import * as http from "http";
import * as https from "https";
import * as path from "path";
import * as fs from "fs";

class Downloader {

    uuid: string = '';
    partSize: number = 0;
    partCount: number = 0;
    partStatus: boolean = false;
    total: number = 0;
    downloaded: number = 0;
    outPath: string = '';
    url: string = '';
    name: string = '';
    progress: number = 0;

    constructor(uuid: string) {
        this.uuid = uuid;
    }

    static build(): Downloader {
        return new Downloader(Downloader.uuid());
    }

    openPart(count: number): Downloader {
        this.partStatus = true;
        this.partCount = count;
        return this;
    }

    closePart(): Downloader {
        this.partStatus = false;
        return this;
    }

    setOutPath(path: string) {
        this.outPath = path;
        return this;
    }

    static getUrlName(url: string): string {
        const start = url.lastIndexOf("/");
        return start !== -1 ? url.substring(start + 1) : url;
    }

    setUrl(url: string) {
        this.url = url;
        this.name = Downloader.getUrlName(this.url);
        return this;
    }

    exe(redirectUrl?: string) {
        // 获取总大小
        https.get(redirectUrl || this.url, {method: 'HEAD'}, res => {
            if (res.statusCode === 302) {
                if (res.headers.location) {
                    this.exe(res.headers.location);
                    return;
                }
            }
            const contentLength = res.headers["content-length"];
            if (!contentLength) {
                console.error('未获取到内容长度');
                return;
            }
            this.total = parseInt(contentLength.toString(), 10);
            console.log(this.total)
            this.partCount = this.total / this.partCount;
            this.download();
        }).on('error', (err) => {
            console.error('Error getting file size:', err);
        });
    }

    private async download() {
        const partPromises = [];
        const partCount = this.partCount;
        const partSize = this.partSize;
        const total = this.total;
        const out = path.join(this.outPath, this.name);
        console.log(out)
        // 分片下载
        for (let i = 0; i < partCount; i++) {
            const start = i * partSize;
            const end = (i === partCount - 1) ? total - 1 : start + partSize - 1;
            partPromises.push(this.downloadPart(start, end));
        }

        // 等待所有部分下载完成
        await Promise.all(partPromises);

        // 合并所有部分到一个文件中
        const outputStream = fs.createWriteStream(out);
        for (let i = 0; i < partCount; i++) {
            const partPath = path.join(__dirname, `part-${i * partSize}-${(i === partCount - 1) ? total - 1 : (i + 1) * partSize - 1}`);
            const partStream = fs.createReadStream(partPath);
            partStream.pipe(outputStream, {end: false});
            partStream.on('end', () => {
                fs.unlink(partPath, (err) => {
                    if (err) console.error(`Error deleting part file: ${err}`);
                });
            });
        }

        outputStream.on('finish', () => {
            console.log('文件下载完成！');
        });
    }

    // 创建下载文件部分的函数
    private downloadPart(start: number, end: number): Promise<void> {
        const url = this.url;
        const downloaded = this.downloaded;
        const total = this.total;
        return new Promise((resolve, reject) => {
            const options = {
                hostname: url,
                path: new URL(url).pathname + `?range=${start}-${end}`,
                method: 'GET',
                headers: {
                    'Range': `bytes=${start}-${end}`,
                    'Content-Type': "application/octet-stream"
                },
            };

            const req = https.get(options, (res) => {
                const partStream = fs.createWriteStream(path.join(__dirname, `part-${start}-${end}`), {flags: 'a'});
                res.pipe(partStream);

                res.on('data', (chunk) => {
                    this.downloaded += chunk.length;
                    this.progress = Math.round((downloaded / total) * 100);
                    console.log(`下载进度: ${this.progress}%`);
                });

                res.on('end', () => {
                    resolve();
                });

                res.on('error', (err) => {
                    reject(err);
                });
            });

            req.on('error', (err) => {
                reject(err);
            });
        });
    }

    isHttps(): boolean {
        return Downloader.getProtocol(this.url) === 'https';
    }

    isHttp(): boolean {
        return Downloader.getProtocol(this.url) === 'http';
    }

    /**
     * 获取地址协议
     * @param url 地址
     */
    static getProtocol(url: string): string {
        // 创建一个新的URL对象
        const urlObj = new URL(url);
        // 返回URL的协议部分
        return urlObj.protocol.slice(0, -1); // 去掉末尾的冒号
    }

    /**
     * 生成UUID
     */
    static uuid(simple?: boolean): string {
        let d = new Date().getTime();
        let template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
        if (simple) {
            template = "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx";
        }
        return template.replace(/[xy]/g, function (c) {
            let r = (d + Math.random() * 16) % 16 | 0;
            d = Math.floor(d / 16);
            return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
        });
    }
}

export default Downloader
